<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>继承的六种方式</title>
</head>

<body>
    <script>
        //继承的六种方式
        //-----------------------------------------------------


        //方式一.原型链 继承
        //     关键:新实例的prototype=父类的实例
        //          优点：1.新实例可继承的属性：实例的构造函数属性，父类构造函数属性，父类的prototype属性
        //                  (注意：不能-继承-父类实例的属性)
        //          缺点：  1.新实例不能向父类构造函数传参
        //                  2.继承单一
        //                  3.所有新实例共享父类实例的属性 
        //                    (因为prototype上的属性共享，一个新实例改变prototype对象上的属性，另一个新实例prototype属性也会改变)
        //                  child1.names.push('yayu');
        //                  console.log(child1.names); // ["kevin", "daisy", "yayu"]
        //                  var child2 = new Child();
        //                  console.log(child2.names); // ["kevin", "daisy", "yayu"]
        //----------------------------
        /* function Parent() {
            this.name = "巴拉巴拉"
        }
        Parent.prototype.sayhi = function () {
            console.log(this.name)
        }
        function Child() { }
        Child.prototype = new Parent()
        var child1 = new Child()
        child1.sayhi() */
        //-----------------------------------------------------


        //方式二.构造函数 继承
        //     关键:用.call或.apply改变this的指向，将父类构造函数引入子类构造函数(在子类函数中做父类函数的自执行，相当于复制)
        //         优点：1.避免了引用类型的属性被所有实例共享 
            //              2.子函数可以向父函数传参 
            //              3.解决了原型链继承方式的3个缺点 
            //              4.可以继承多个构造函数=>.call多个
        //          缺点：1.只能继承父类构造函数的属性
        //               2.无法实现构造函数的复用(每次都要重新调用)
        //               3.每个新实例都有父类构造函数的副本，臃肿(不符合DRY规则，Don't repeat yourself)
        //----------------------------
        /* function Parent() {
            this.name = "巴拉巴拉"
            this.colors = ["yellow", "blue", "purple"]
        }
        Parent.prototype.sayhi = function () {
            console.log(this.name)
        }
        function Child(age) {
            Parent.call(this)
            this.age = age
        }
        var child1 = new Child(16)
        var child2 = new Child(18)
        child1.colors.push("red")
        console.log(child1.name)//巴拉巴拉
        console.log(child2.name)//巴拉巴拉
        console.log(child1.colors)//(4) ["yellow", "blue", "purple", "red"]
        console.log(child2.colors)//(3) ["yellow", "blue", "purple"] */
        //-----------------------------------------------------


        //方式三.组合继承=>组合原型链及构造函数方式
        //     关键:结合两种模式优点：传参和复用
        //         优点：1.可以继承了父类原型上的属性，可传参可复用
        //              2.每个新实例引入的构造函数是私有的
        //          缺点：1.调用两次父类构造函数(耗内存)，子类的构造函数会代替原型上的父类构造函数
        //----------------------------
        /* function Parent() {
            this.name = name
            this.colors = ["yellow", "blue", "purple"]
        }
        Parent.prototype.sayhi = function () {
            console.log(this.name)
        }
        function Child(name, age) {
            Parent.call(this, name)//第二次调用
            this.age = age
        }
        Child.prototype = new Parent()//第一次调用

        var child1 = new Child("张三", 16)
        var child2 = new Child("李四", 18)
        console.log(child1.name)
        console.log(child2.name)
        console.log(child1.age)//16
        console.log(child2.age)//18 */
        //-----------------------------------------------------


        //方式四.原型式继承
        //     关键:用一个函数包装一个对象，然后返回这个函数的调用，这个函数就变成了可以随意增添属性的实例或对象，object.create()的原理
        //         优点：1.类似于复制一个对象，用函数进行包装
        //          缺点：1.所有实例都会继承原型上的属性。
        //                2.无法实现复用(新实例的属性是后加的)
        //----------------------------
/*         function createObj(o) {//也是ES6以后，Object.create()方法的原理
            function F() { }
            F.prototype = o;
            console.log(o._proto_ === Object.prototype)//false
            console.log(F.prototype.constructor === Object)//true
            return new F()
        }
        var person = {
            name: "张三",
            friends: ["李四", "丁一"]
        }
        var person1 = createObj(person)
        //var person2 = createObj(person)

        person1.name = "person1"
        //console.log(person2.name)//张三
        person1.friends.push("赵五")
        //console.log(person2.friends)//(3) ["李四", "丁一", "赵五"]
        //console.log(person)//{name: "张三", friends: Array(3)}

        person1.friends=["大佬"]
        //console.log(person1.friends)//["大佬"]
        //console.log(person.friends)//(3) ["李四", "丁一", "赵五"] */
        //-----------------------------------------------------


        //方式五.寄生式继承
        //     关键:给原型式继承套个函数
        //         优点：1.没有自定义类型，只是套了个壳子返回对象，这个函数就成了创建的新对象
        //          缺点：1.没用到原型，无法复用
        //----------------------------
        /* var obj = {
            name: "张三",
            friends: ["李四", "王五"]
        }
        function createObj(o) {
            function F() { }
            F.prototype = o
            return new F()
        }
        function Child() { }
        var child1 = new createObj(obj)
        var child2 = Object.create(obj)
        console.log(child1.name)//张三
        console.log(child2.name)//张三

        function createChild(o){    //核心：
            var newobj = createObj(o)   //创建对象，等价于Object.create(o)
            newobj.sayhi=function(){    //增强对象
                console.log("hi")
            }
            return newobj   //指定对象
        }
        var p1 = createChild(obj)
        p1.sayhi()//hi */
        //-----------------------------------------------------


        //方式六.寄生组合式继承(常用)
        //     关键:解决了组合继承调用两次父类构造函数的问题
        //----------------------------
        /* function Parent(name) {
            this.name = name
            this.colors = ["red", "blue", "green"]
        }
        Parent.prototype.sayhi = function () {
            console.log(this.name)
        }
        function Child(name, age) {
            Parent.call(this, name)
            this.age = age
        }
        function createObj(o) {
            function F() { }
            F.prototype = o
            return new F()
        }
        //Child.prototype=new Parent()换成以下
        function prototype(child, parent) {
            var prototype = createObj(parent.prototype)
            prototype.constructor = child
            child.prototype = prototype
        }
        prototype(Child, Parent)

        var child1 = new Child("小白", 16)
        console.log(child1)//Child {name: "小白", colors: Array(3), age: 16} */
        //-----------------------------------------------------
    </script>

</body>

</html>